package com.dynamic.mybatis.core.metadata;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.dynamic.mybatis.core.enums.CommandType;
import com.dynamic.mybatis.core.enums.RequestMethod;
import com.dynamic.mybatis.core.enums.ReturnType;
import com.dynamic.mybatis.core.parser.NamespaceHelper;
import com.dynamic.mybatis.core.utils.StringUtils;
import com.dynamic.mybatis.core.utils.TreeUtil;
import org.apache.ibatis.mapping.SqlCommandType;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description
 * @Author xs
 * @Date 2021/3/26 14:29
 */
public class DynamicMappedStatement implements Serializable {
    /**
     * 持久化后的唯一主键,防止 namespace+shortId 重复问题,uid 存在时修改，不存在新增。
     */
    protected String uid;//uid
    protected String parentId;
    protected Integer treeId;
    protected Integer treeParentId;
    /**
     * 对应 mybatis Statement id
     * namespace + "." + shortId
     */
    protected String id;
    protected String shortId;


    private String name;
    protected String referenceId;
    protected CommandType commandType;//sql类型

    protected Collection<String> tables;
    protected String parameterWrapper;//实际参数所在的 参数的位置 json path  a.b.c
    protected String resultWrapper;//实际参数所在的 参数的位置 json path  a.b.c
    protected String namespace;//用来解决 不同产品/项目id重复
    //https://blog.csdn.net/qq_42093488/article/details/124956848 批处理
    protected boolean batchExecute;//是否批量执行，执行时parameter类型应是Collection,否则按单执行处理
    protected int batchSize;//每次提交数量，
    protected boolean transaction;//是否开启事务
    protected ReturnType returnType;
    protected boolean sessionContext = Boolean.FALSE; //是否将结果集作为用户session上下文,并将该接口参数转换为token作为结果集
    protected boolean enable = Boolean.TRUE;//是否可用
    protected String sqlStatement;//mybatis sql语句
    protected String parameterType;//xml parameterType属性 默认为HashMap
    protected String parameterMap;//xml parameterMap属性
    protected String resultType;//xml resultType属性
    protected String resultMap;//xml resultMap属性
    protected Object targetDataSourceKey;//默认数据源key
    protected Object[] dynamicDataSourceKeys;//多数据源key
    protected Map<String, Object> attributes = new HashMap<>();
    protected List<DynamicMappedStatement> children;

    //rest接口参数
    protected List<MappedStatementMetaField> requestHeader;
    protected List<MappedStatementMetaField> inputParameter;
    protected List<MappedStatementMetaField> outputParameter;
    protected boolean rest = Boolean.TRUE;//是否暴露为web接口
    protected boolean auth = Boolean.TRUE;//权限认证
    protected RequestMethod method;
    protected Object inputParameterMock;
    protected Object outputParameterMock;

    protected LocalDateTime createTime;
    protected LocalDateTime updateTime;
    protected String createBy;
    protected String updateBy;

    public String getDynamicMappedStatementNamespace() {
        return NamespaceHelper.getNamespace(namespace);
    }

    public String getId() {
        return NamespaceHelper.getDynamicMappedStatementId(namespace, shortId);
    }

    public CommandType getCommandType() {
        return commandType;
    }

    public ReturnType getReturnType() {
        return returnType;
    }


    public String getParameterType() {
        return parameterType;
    }

    public String getParameterMap() {
        return parameterMap;
    }

    public String getResultType() {
        return resultType;
    }

    public String getResultMap() {
        return resultMap;
    }

    public Object getTargetDataSourceKey() {
        return targetDataSourceKey;
    }

    public Object[] getDynamicDataSourceKeys() {
        return dynamicDataSourceKeys;
    }

    public Map<String, Object> getAttributes() {
        return attributes;
    }

    public String getNamespace() {
        return namespace;
    }

    public String getShortId() {
        return shortId;
    }

    public String getName() {
        return name;
    }

    public boolean isBatchExecute() {
        return batchExecute;
    }

    public int getBatchSize() {
        return batchSize;
    }

    public String getParameterWrapper() {
        return parameterWrapper;
    }

    public String getUid() {
        return uid;
    }

    public List<DynamicMappedStatement> getChildren() {
        return children;
    }

    public boolean isTransaction() {
        return transaction;
    }

    public String getSqlStatement() {
        return sqlStatement;
    }

    public String getParentId() {
        return parentId;
    }

    public String getReferenceId() {
        return referenceId;
    }

    public String getResultWrapper() {
        return resultWrapper;
    }

    public List<MappedStatementMetaField> getInputParameter() {
        return inputParameter;
    }

    public List<MappedStatementMetaField> getOutputParameter() {
        return outputParameter;
    }

    public RequestMethod getMethod() {
        return method;
    }

    public void setShortId(String shortId) {
        this.shortId = shortId;
    }

    public void setId(String id) {
        this.id = id;
    }

    public void setParentId(String parentId) {
        this.parentId = parentId;
    }

    public void setUid(String uid) {
        this.uid = uid;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setReferenceId(String referenceId) {
        this.referenceId = referenceId;
    }


    public void setCommandType(CommandType commandType) {
        this.commandType = commandType;
    }

    public void setCommandType(String commandType) {
        this.commandType = CommandType.getCommandType(commandType);
    }

    public void setParameterWrapper(String parameterWrapper) {
        this.parameterWrapper = parameterWrapper;
    }

    public void setResultWrapper(String resultWrapper) {
        this.resultWrapper = resultWrapper;
    }

    public void setNamespace(String namespace) {
        this.namespace = namespace;
    }

    public void setBatchExecute(boolean batchExecute) {
        this.batchExecute = batchExecute;
    }

    public void setBatchSize(int batchSize) {
        this.batchSize = batchSize;
    }

    public void setTransaction(boolean transaction) {
        this.transaction = transaction;
    }

    public void setReturnType(ReturnType returnType) {
        this.returnType = returnType;
    }

    public void setReturnType(String returnType) {
        this.returnType = ReturnType.getReturnType(returnType);
    }

    public void setSqlStatement(String sqlStatement) {
        this.sqlStatement = sqlStatement;
    }

    public void setParameterType(String parameterType) {
        this.parameterType = parameterType;
    }

    public void setParameterMap(String parameterMap) {
        this.parameterMap = parameterMap;
    }

    public void setResultType(String resultType) {
        this.resultType = resultType;
    }

    public void setResultMap(String resultMap) {
        this.resultMap = resultMap;
    }

    public void setTargetDataSourceKey(Object targetDataSourceKey) {
        this.targetDataSourceKey = targetDataSourceKey;
    }

    public void setDynamicDataSourceKeys(Object[] dynamicDataSourceKeys) {
        this.dynamicDataSourceKeys = dynamicDataSourceKeys;
    }

    public void setAttributes(Map<String, Object> attributes) {
        this.attributes = attributes;
    }

    public void setChildren(List<DynamicMappedStatement> children) {
        this.children = children;
    }

    public void setInputParameter(List<MappedStatementMetaField> inputParameter) {
        this.inputParameter = inputParameter;
    }

    public void setOutputParameter(List<MappedStatementMetaField> outputParameter) {
        this.outputParameter = outputParameter;
    }

    public void setMethod(RequestMethod method) {
        this.method = method;
    }

    public void setMethod(String method) {
        this.method = RequestMethod.getRequestMethod(method);
    }


    public Object getInputParameterMock() {
        return inputParameterMock;
    }

    public void setInputParameterMock(Object inputParameterMock) {
        this.inputParameterMock = inputParameterMock;
    }

    public Object getOutputParameterMock() {
        return outputParameterMock;
    }

    public void setOutputParameterMock(Object outputParameterMock) {
        this.outputParameterMock = outputParameterMock;
    }

    public Integer getTreeId() {
        return treeId;
    }

    public void setTreeId(Integer treeId) {
        this.treeId = treeId;
    }

    public Integer getTreeParentId() {
        return treeParentId;
    }

    public void setTreeParentId(Integer treeParentId) {
        this.treeParentId = treeParentId;
    }

    public boolean isAuth() {
        return auth;
    }

    public void setAuth(boolean auth) {
        this.auth = auth;
    }

    public boolean isRest() {
        return rest;
    }

    public void setRest(boolean rest) {
        this.rest = rest;
    }

    public boolean isEnable() {
        return enable;
    }

    public void setEnable(boolean enable) {
        this.enable = enable;
    }

    public boolean isSessionContext() {
        return sessionContext;
    }

    public void setSessionContext(boolean sessionContext) {
        this.sessionContext = sessionContext;
    }

    public List<MappedStatementMetaField> getRequestHeader() {
        return requestHeader;
    }

    public void setRequestHeader(List<MappedStatementMetaField> requestHeader) {
        this.requestHeader = requestHeader;
    }

    public LocalDateTime getCreateTime() {
        return createTime;
    }

    public void setCreateTime(LocalDateTime createTime) {
        this.createTime = createTime;
    }

    public LocalDateTime getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(LocalDateTime updateTime) {
        this.updateTime = updateTime;
    }

    public String getCreateBy() {
        return createBy;
    }

    public void setCreateBy(String createBy) {
        this.createBy = createBy;
    }

    public String getUpdateBy() {
        return updateBy;
    }

    public void setUpdateBy(String updateBy) {
        this.updateBy = updateBy;
    }

    public Collection<String> getTables() {
        return tables;
    }

    public void setTables(Collection<String> tables) {
        this.tables = tables;
    }

    public Builder toBuilder() {
        return new Builder(this);
    }

    public static class Builder {
        public Builder() {
        }

        public Builder(DynamicMappedStatement parent) {
            this.parent = parent;
        }

        private DynamicMappedStatement parent;

        public Builder setShortId(String shortId) {
            parent.shortId = shortId;
            return this;
        }

        public Builder setId(String id) {
            parent.id = id;
            return this;
        }

        public Builder setUid(String uid) {
            parent.uid = uid;
            return this;
        }

        public Builder setName(String name) {
            parent.name = name;
            return this;
        }

        public Builder setCommandType(CommandType commandType) {
            parent.commandType = commandType;
            return this;
        }

        public Builder setCommandType(SqlCommandType sqlCommandType) {
            parent.commandType = CommandType.getCommandType(sqlCommandType.name());
            return this;
        }

        public Builder setCommandType(String commandType) {
            parent.commandType = CommandType.getCommandType(commandType);
            return this;
        }

        public Builder setParameterWrapper(String parameterWrapper) {
            parent.parameterWrapper = parameterWrapper;
            return this;
        }

        public Builder setNamespace(String namespace) {
            parent.namespace = namespace;
            return this;
        }

        public Builder setBatchExecute(boolean batchExecute) {
            parent.batchExecute = batchExecute;
            return this;
        }

        public Builder setBatchSize(int batchSize) {
            parent.batchSize = batchSize;
            return this;
        }

        public Builder setTransaction(boolean transaction) {
            parent.transaction = transaction;
            return this;
        }

        public Builder setReturnType(ReturnType returnType) {
            parent.returnType = returnType;
            return this;
        }

        public Builder setReturnType(String returnType) {
            parent.returnType = ReturnType.getReturnType(returnType);
            return this;
        }

        public Builder setSqlStatement(String sqlStatement) {
            parent.sqlStatement = sqlStatement;
            return this;
        }

        public Builder setParameterType(String parameterType) {
            parent.parameterType = parameterType;
            return this;
        }

        public Builder setParameterMap(String parameterMap) {
            parent.parameterMap = parameterMap;
            return this;
        }

        public Builder setResultType(String resultType) {
            parent.resultType = resultType;
            return this;
        }

        public Builder setResultMap(String resultMap) {
            parent.resultMap = resultMap;
            return this;
        }

        public Builder setTargetDataSourceKey(Object targetDataSourceKey) {
            parent.targetDataSourceKey = targetDataSourceKey;
            return this;
        }

        public Builder setDynamicDataSourceKeys(Object[] dynamicDataSourceKeys) {
            parent.dynamicDataSourceKeys = dynamicDataSourceKeys;
            return this;
        }

        public Builder setAttributes(Map<String, Object> attributes) {
            parent.attributes = attributes;
            return this;
        }

        public Builder setChildren(List<DynamicMappedStatement> children) {
            parent.children = children;
            return this;
        }

        public Builder setResultWrapper(String resultWrapper) {
            parent.resultWrapper = resultWrapper;
            return this;
        }

        public Builder setReferenceId(String referenceId) {
            parent.referenceId = referenceId;
            return this;
        }

        public Builder setParentId(String parentId) {
            parent.parentId = parentId;
            return this;
        }

        public Builder setInputParameter(MappedStatementMetaField inputParameter) {
            parent.inputParameter = Collections.singletonList(inputParameter);
            return this;
        }

        public Builder setInputParameter(List<MappedStatementMetaField> inputParameter) {
            parent.inputParameter = inputParameter;
            return this;
        }

        public Builder setOutputParameter(MappedStatementMetaField outputParameter) {
            parent.outputParameter = Collections.singletonList(outputParameter);
            return this;
        }

        public Builder setOutputParameter(List<MappedStatementMetaField> outputParameter) {
            parent.outputParameter = outputParameter;
            return this;
        }

        public Builder setMethod(RequestMethod method) {
            parent.method = method;
            return this;
        }

        public Builder setMethod(String method) {
            parent.method = RequestMethod.getRequestMethod(method);
            return this;
        }

        public Builder setInputParameterMock(Object inputParameterMock) {
            parent.inputParameterMock = inputParameterMock;
            return this;
        }

        public Builder setOutputParameterMock(Object outputParameterMock) {
            parent.outputParameterMock = outputParameterMock;
            return this;
        }

        public Builder setTreeId(Integer treeId) {
            parent.treeId = treeId;
            return this;
        }

        public Builder setTreeParentId(Integer treeParentId) {
            parent.treeParentId = treeParentId;
            return this;
        }

        public Builder setAuth(boolean auth) {
            parent.auth = auth;
            return this;
        }

        public Builder setRest(boolean rest) {
            parent.rest = rest;
            return this;
        }

        public Builder setEnable(boolean enable) {
            parent.enable = enable;
            return this;
        }

        public Builder setSessionContext(boolean sessionContext) {
            parent.sessionContext = sessionContext;
            return this;
        }

        public Builder setRequestHeader(List<MappedStatementMetaField> requestHeader) {
            parent.requestHeader = requestHeader;
            return this;
        }


        public Builder setCreateTime(LocalDateTime createTime) {
            parent.createTime = createTime;
            return this;
        }


        public Builder setUpdateTime(LocalDateTime updateTime) {
            parent.updateTime = updateTime;
            return this;
        }


        public Builder setCreateBy(String createBy) {
            parent.createBy = createBy;
            return this;
        }

        public Builder setUpdateBy(String updateBy) {
            parent.updateBy = updateBy;
            return this;
        }

        public Builder setTables(Collection<String> tables) {
            parent.tables = tables;
            return this;
        }

        public DynamicMappedStatement builder() {
            parent.namespace = NamespaceHelper.getNamespace(parent.namespace);
            parent.id = NamespaceHelper.getDynamicMappedStatementId(parent.namespace, Objects.toString(parent.shortId, parent.id));
            String[] strs = parent.id.split("\\.");
            parent.shortId = strs[strs.length - 1];
            if (StringUtils.isBlank(parent.getMethod())) {
                parent.method = parent.getCommandType() == CommandType.StandardCommand.SELECT ? RequestMethod.StandardRequestMethod.GET : RequestMethod.StandardRequestMethod.POST;
            }

            if (CollectionUtils.isEmpty(parent.requestHeader)) {
                if (parent.isAuth()) {
                    parent.requestHeader = parent.requestHeader == null ? new ArrayList<>() : parent.requestHeader;

                    MappedStatementMetaField authField = new MappedStatementMetaField();
                    authField.setField("Authorization");
                    authField.setName("认证");
                    MappedStatementFieldValidator validator = new MappedStatementFieldValidator();
                    validator.setEnable(Boolean.TRUE);
                    validator.setRequired(parent.auth);
                    authField.setValidator(validator);
                    parent.requestHeader.add(authField);

                    parent.setRequestHeader(TreeUtil.setTree(parent.requestHeader));
                }
            } else {
                parent.requestHeader = parent.getRequestHeader().stream().filter(f ->
                        StringUtils.isNotBlank(f.getField()) && (parent.auth || (!parent.isAuth() && !Objects.equals("Authorization", f.getField())))
                ).collect(
                        Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(MappedStatementMetaField::getField))
                        ), ArrayList::new));

            }
            return parent;
        }
    }


}